/** \file    timing.cpp
  * \brief   Gives a means for timing a program with details for routines call-tree.
  * \date    2003
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/mswin/src/timing.cpp,v 1.11 2006/03/05 06:31:18 cscom1r4 Exp $
  */

#include "mswin/timing.h"
#include "timing_node.h"
#include <algorithm>
#include <iomanip>
#include <xtd/minmax>

namespace mswin {

node::~node()
{
	std::for_each(children.begin(), children.end(), deleter());
}

node & node::operator = (const node & rhs)
{
	name = rhs.name;
	calls_count = rhs.calls_count;
	node_time = rhs.node_time;
	subtree_time = rhs.subtree_time;
	entrance_time = rhs.entrance_time;
	node_count = rhs.node_count;
	subtree_count = rhs.subtree_count;
	entrance_count = rhs.entrance_count;

	children.clear();
	children_type::const_iterator i;
	for (i = rhs.children.begin(); i != rhs.children.end(); ++i)
		*open(i->first) = *i->second;

	return * this;
}

node & node::operator += (const node & rhs)
{
	calls_count += rhs.calls_count;
	node_time += rhs.node_time;
	subtree_time += rhs.subtree_time;
	//entrance_time += rhs.entrance_time;
	node_count.QuadPart += rhs.node_count.QuadPart;
	subtree_count.QuadPart += rhs.subtree_count.QuadPart;
	//entrance_count += rhs.entrance_count;

	children_type::const_iterator i;
	for (i = rhs.children.begin(); i != rhs.children.end(); ++i)
		*open(i->first) += *i->second;

	return * this;
}

node * node::open(const std::string & name)
{
	node * & opening = children[name];
	if (opening == 0)
		opening = new node(name);
	return opening;
}

void node::enumerate_children(nodes_enumerated & v, int nesting) const
{
	children_type::const_iterator i;
	for (i = children.begin(); i != children.end(); ++i) {
		v.push_back(node_info(nesting, i->second));
		i->second->enumerate_children(v, nesting + 1);
	}
}

// timing_tree

timing_tree::timing_tree()
{
	_current =_root = new node("root");
}

timing_tree::timing_tree(const timing_tree & rhs)
{
	_current =_root = new node("root");
	*_root = *rhs._root;
}

timing_tree::~timing_tree()
{
	delete _root;
}

timing_tree & timing_tree::operator = (const timing_tree & rhs)
{
	clear();
	*_root = *rhs._root;

	return * this;
}

timing_tree & timing_tree::operator += (const timing_tree & rhs)
{
	*_root += *rhs._root;
	return * this;
}

void timing_tree::clear()
{
	assert(_current == _root);
	delete _root;
	_current = _root = new node("root");
}

timing_mark::timing_mark(const timing_tree & tree, const std::string & node_name) :
	_tree(tree)
{
	init(node_name);
}

timing_mark::timing_mark(const timing_tree * tree, const std::string & node_name) :
	_tree(*tree)
{
	if (tree)
		init(node_name);
	else
		_parent = 0;
}

inline void timing_mark::init(const std::string & node_name)
{
	_parent = _tree._current;
	_tree._current = _tree._current->open(node_name);

	time creation, exit, kernel, user;
	GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user);
	_tree._current->entrance_time = kernel + user;

	QueryPerformanceCounter(&_tree._current->entrance_count);
}

timing_mark::timing_mark(timing_mark & snatch) :
	_tree(snatch._tree)
{
	_parent = snatch._parent;
	snatch._parent = 0;
}

timing_mark::~timing_mark()
{
	finish();
}

void timing_mark::finish()
{
	if (!_parent)
		return;

	time creation, exit, kernel, user;
	GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user);
	time elapsed_time = kernel + user - _tree._current->entrance_time;
	_tree._current->node_time += elapsed_time;
	_tree._current->subtree_time += elapsed_time;

	LARGE_INTEGER elapsed_count;
	QueryPerformanceCounter(&elapsed_count);
	elapsed_count.QuadPart -= _tree._current->entrance_count.QuadPart;
	_tree._current->node_count.QuadPart += elapsed_count.QuadPart;
	_tree._current->subtree_count.QuadPart += elapsed_count.QuadPart;

	++_tree._current->calls_count;

	_tree._current = _parent;
	if (_tree._current != _tree._root) {
		_tree._current->node_time -= elapsed_time;
		_tree._current->node_count.QuadPart -= elapsed_count.QuadPart;
	}
	_parent = 0;
}

double timing_mark::seconds() const
{
	time creation, exit, kernel, user;
	GetThreadTimes(GetCurrentThread(), &creation, &exit, &kernel, &user);
	return (kernel + user - _tree._current->entrance_time).seconds();
}

std::ostream & operator << (std::ostream & out, const timing_tree & t)
{
	nodes_enumerated v;
	t._root->enumerate_children(v, 0);
	if (v.empty())
		return out;

	LARGE_INTEGER freq;
	QueryPerformanceFrequency(&freq);
	float period = 1.0f / freq.QuadPart;
	//out << "counter frequency " << freq.QuadPart << '\n';

	size_t i;
	size_t max_action_name = 6;
	for (i = 0; i != v.size(); ++i)
		xtd::max<size_t>(max_action_name, v[i].ptr->name.size() + v[i].nesting);

	out << "Action" << std::string(max_action_name-6, ' ') << "   Count  Time(cpu)  Tree(cpu)  Time(wld)  Tree(wld)\n"
		<< "------" << std::string(max_action_name-6, ' ') << "   -----  ---------  ---------  ---------  ---------\n";

	std::ios_base::fmtflags safe_flags = out.flags();
	out.setf(std::ios::fixed);
	int safe_prec = out.precision(3);

	for (i = 0; i != v.size(); ++i) {
		const node & n = *v[i].ptr;
		out << std::string(v[i].nesting, ' ')
			<< n.name
			<< std::string(max_action_name - v[i].nesting - n.name.size(), ' ')
			<< std::setw(8) << n.calls_count
			<< std::setw(11) << n.node_time;
		if (n.has_child())
			out << std::setw(11) << n.subtree_time;
		else
			out << "           ";
		out	<< std::setw(11) << period * n.node_count.QuadPart;
		if (n.has_child())
			out << std::setw(11) << period * n.subtree_count.QuadPart;
		else
			out << "           ";
		out	<< '\n';
	}

	out.precision(safe_prec);
	out.flags(safe_flags);

	return out;
}

} //namespace mswin

